/**
 * Copyright 2006 Brad Reynolds
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.leeloo.databinding.ui;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.leeloo.core.Tag;
import org.leeloo.core.TagService;
import org.leeloo.databinding.internal.ui.TagFilter;

/**
 * Manages the resposibility of tracking tags, their selection, and the tags
 * being applied as filters in a viewer. All methods may be invoked from any
 * thread.
 * 
 * @author Brad Reynolds
 */
public class TagFilterManager {
    /**
     * Convenience value to represent no tags.
     */
    private final static Tag[] NO_TAGS = new Tag[0];

    /**
     * Tags that have been {@link #addTag(Tag) added} to the manager.
     */
    private final Set tags = new HashSet();

    /**
     * Tags that are selected and have filters applied to {@link #tableViewer}.
     */
    private final Set selectedTags = new HashSet();

    /**
     * TableViewer to filter.
     */
    private final TableViewer tableViewer;

    /**
     * TagService to use to lookup tags for elements.
     */
    private final TagService tagService;

    /**
     * Constructs a new instance.
     * 
     * @param viewer
     * @param tagService
     */
    public TagFilterManager(TableViewer viewer, TagService tagService) {
        if (viewer == null) {
            throw new IllegalArgumentException("Parameter viewer was null."); //$NON-NLS-1$
        }
        if (tagService == null) {
            throw new IllegalArgumentException("Parameter tagService was null."); //$NON-NLS-1$
        }

        this.tableViewer = viewer;
        this.tagService = tagService;
    }

    /**
     * Adds a tag to the manager. If a duplicate is added it is ignored.
     * 
     * @param tag
     */
    public void addTag(Tag tag) {
        if (tag == null) {
            throw new IllegalArgumentException("Parameter tag was null."); //$NON-NLS-1$
        }

        if (!tags.contains(tag)) {
            tags.add(tag);
        }
    }

    /**
     * @return selected tags
     */
    public Tag[] getSelection() {
        return (Tag[]) ((selectedTags.size() > 0) ? selectedTags.toArray(new Tag[selectedTags.size()]) : NO_TAGS);
    }

    /**
     * @return registered tags
     */
    public Tag[] getTags() {
        return (Tag[]) ((tags.size() > 0) ? tags.toArray(new Tag[tags.size()]) : NO_TAGS);
    }

    /**
     * Sets the tags that are to be selected. Every invocation removes the
     * existing filters on the viewer and creates new filters.
     * 
     * @param selection
     *            <code>null</code> to remove selection
     * @throws IllegalArgumentException
     *             if a tag is to be selected but has not been added to the
     *             manager
     */
    public void setSelection(final Tag[] selection) {
        if (selection != null) {
            for (int i = 0; i < selection.length; i++) {
                if (!this.tags.contains(selection[i])) {
                    throw new IllegalArgumentException("Tag [" + selection[i] + "] has not been added to the manager."); //$NON-NLS-1$//$NON-NLS-2$
                }
            }
        }

        tableViewer.getControl().getDisplay().syncExec(new Runnable() {
            public void run() {
                ViewerFilter[] filters = tableViewer.getFilters();
                for (int i = 0; i < filters.length; i++) {
                    tableViewer.removeFilter(filters[i]);
                }

                selectedTags.clear();
                if (selection != null) {
                    selectedTags.addAll(Arrays.asList(selection));
                    for (Iterator it = selectedTags.iterator(); it.hasNext();) {
                        Tag tag = (Tag) it.next();
                        tableViewer.addFilter(new TagFilter(tagService, tag));
                    }
                }
            }
        });
    }

    /**
     * Removes all tags and selection from the manager.
     */
    public void clear() {
        tags.clear();
        setSelection(null);
    }
}
